<?php
/**
 * Created by PhpStorm.
 * User: krystal
 * Date: 2019/4/18
 * Time: 13:31
 */

namespace app\zyhsapi\lib;

use app\common\lib\exception\ApiException;
use app\zyhsapi\model\ZyhsConfig;
use app\zyhsapi\model\ZyhsUserAccount;
use app\zyhsapi\model\ZyhsUserAccountRecord;
use app\zyhsapi\pay\Ali;
use app\zyhsapi\pay\Weixin;
use Exception;
use think\Db;
use think\Log;

/**
 * 订单拓展类库
 * Class Orders
 * @package app\common\lib
 */
class OrdersLib
{

    /**
     *  * 统一三方下单入口
     * @param array $map
     * @param int $goods_type //1=>虚拟,2=>实体
     * @return array|bool
     * @throws Exception
     */
    public static function unifyPayOrder($map = [], $goods_type = 1)
    {
        $trade_type = (int)$map['trade_type'];
        $pay_des = $map['pay_des'];
        $total_amount = $map['total_amount'];
        $order_id = $map['order_id'];
        $openid = $map['openid'];
        $time = time();
        $callback_url = $map['callback_url'];
        $order_type = (int)$map['order_type'];
        $arr = [
            'status' => false,
            'data' => [],
        ];
        //        支付订单
        switch (true)
        {
            // 支付宝APP支付 支付类型为 APP
            case $trade_type === 1:
                $total_amount = ($total_amount);// 以元为单位的
                $AliPay = new Ali();
                $pay_url = $AliPay->pay($pay_des, $order_id, $total_amount, 'APP', '', $goods_type);
                $arr = [
                    'status' => true,
                    'data' => [
                        'pay_url' => $pay_url,
                        'order_id' => $order_id,
                    ],
                ];
                break;
            // 微信支付 2=>APP支付  6=>JSAPI支付 8=>H5 13=>小程序支付
            case in_array($trade_type, [2, 6, 8, 13, 15], true):
//                $total_amount = sprintf('%u', $total_amount * 100);// 以分为单位的
                // 修复小数点精度丢失问题
                $total_amount = sprintf('%u', bcmul($total_amount, 100));// 以分为单位的
                $WxPay = new Weixin();
                // 获取支付类型 APP、JSAPI 或 H5
                $accountConfig = self::getOrderAccountType($order_id, 2);
                $wxConfig = self::getPayConfig('wx', $accountConfig['account']);

                if (empty($wxConfig))
                {
                    return $arr;
                }

                $data = $WxPay->pay($pay_des, $order_id, $total_amount, 'APP', $wxConfig, $openid, $goods_type);
                // 下单成功
                if ($data['return_code'] === 'SUCCESS' && $data['result_code'] === 'SUCCESS')
                {
                    switch ($trade_type)
                    {
                        // APP支付
                        case 2:
                            $sign['appid'] = $wxConfig['appid'];
                            $sign['partnerid'] = $wxConfig['mchid'];
                            $sign['prepayid'] = $data['prepay_id'];
                            $sign['package'] = 'Sign=WXPay';
                            $sign['noncestr'] = $WxPay->getRandChar(32);
                            $sign['timestamp'] = $time;
                            $sign['sign'] = $WxPay->getSign($sign, $wxConfig);
                            $sign['order_id'] = $order_id;

                            $data['nonce_str'] = $sign['noncestr'];
                            $data['sign'] = $sign['sign'];

                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        // JSAPI支付
                        case 13:
                        case 15:
                        case 6:
                            $sign = [
                                'appId' => $wxConfig['appid'],
                                'timeStamp' => $time,
                                'nonceStr' => $WxPay->getRandChar(32),
                                'package' => 'prepay_id=' . $data['prepay_id'],
                                'signType' => 'MD5',
                            ];
                            $sign['paySign'] = $WxPay->getSignOfJsApi($sign, $wxConfig);

                            $sign['order_id'] = $order_id;
                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        // H5支付返回
                        case 8:
                            $sign = [
                                'mweb_url' => $data['mweb_url'] . '&redirect_url=' . ($order_type === 1 ? $callback_url : urlencode($callback_url) . $order_id),
                                'order_id' => $order_id,
                            ];

                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        default:
                            break;
                    }
                } else
                {
                    // $ret = [
                    //     'return_code' => $data['return_code'],
                    //     'return_msg' => $data['return_msg'],
                    // ];
                    $arr = [
                        'status' => false,
                        'data' => $data,
                    ];
                }
                break;
            // 京东支付
            case $trade_type === 5:
//                $total_amount = sprintf("%u", $total_amount * 100);// 以分为单位的
//                $JdPay = new Jd();
//                $data = $JdPay->pay($pay_des, $order_id, $total_amount,$goods_type);
//                if ($data === false)
//                {
//                    $arr = [
//                        'status' => false,
//                        'data' => $data
//                    ];
//                } else
//                {
//                    $arr = [
//                        'status' => true,
//                        'data' => $data
//                    ];
//                }


                $data = [
                    'order_id' => $order_id,
                    'return_code' => 0,
                    'return_msg' => '京东支付支付错误，非常抱歉！',
                ];
                $arr = [
                    'status' => false,
                    'data' => $data,
                ];

                break;
            // 支付宝手机网页支付 支付类型为 WAP
            case $trade_type === 7:
                $total_amount = ($total_amount);// 以元为单位的
                $AliPay = new Ali();
//                $payForm = $AliPay->pay($pay_des, $order_id, $total_amount, 'WAP', $order_type === 1 ? $callback_url : urlencode(urldecode($callback_url) . $order_id), $goods_type);
                //注意：一定不要urlencode，否则 用户点击完成，跳转地址为“https://mclient.alipay.com/h5/”+$callback_url（这尼玛上哪说理去）
                $payForm = $AliPay->pay($pay_des, $order_id, $total_amount, 'WAP', $callback_url, $goods_type);

                $arr = [
                    'status' => true,
                    'data' => [
                        'par_form' => $payForm,
                        'order_id' => $order_id,
                    ],
                ];
                break;
            case $trade_type === 9:
                $arr = [
                    'status' => true,
                    'data' => [
                        'order_id' => $order_id,
                    ],
                ];
                break;
            case in_array($trade_type, [10, 11], true)://字节跳动小程序支付 10=>支付宝APP 11=>微信H5
                //商户配置
                $config_tt = config('miniapp')['TT'];

                $orderInfo = [
                    'merchant_id' => $config_tt['merchant']['merchant_id'],
                    'app_id' => $config_tt['merchant']['app_id'],
                    'sign_type' => 'MD5',
                    'timestamp' => time(),
                    'version' => '2.0',
                    'trade_type' => 'H5',
                    'product_code' => 'pay',
                    'payment_type' => 'direct',
                    'out_order_no' => $order_id,
                    'total_amount' => (int)(sprintf('%u', bcmul($total_amount, 100))),//金额，整型，单位：分（不能有小数）
                    'currency' => 'CNY',
                    'trade_time' => time(),
                    'valid_time' => '300',
                    'notify_url' => 'https://www.goddessxzns.com/',
                    'body' => $map['pay_des'],
                    'subject' => $map['pay_des'],//商户订单名称；如果是测试接入，请务必填写：xxx 业务xx 活动测试，例如，懂车帝xx 活动测试
                    'uid' => $map['uid'],
                ];
                if ($trade_type === 10)
                {
                    //支付宝APP支付
                    $AliPay = new Ali();
                    $pay_url = $AliPay->pay($pay_des, $order_id, $total_amount, 'APP', '', $goods_type);
                    $orderInfo['alipay_url'] = $pay_url;
                } else
                {
                    // 微信h5支付
                    $total_amount = sprintf('%u', bcmul($total_amount, 100));// 以分为单位的
                    $WxPay = new Weixin();
                    // 获取支付类型 APP、JSAPI 或 H5
                    $accountConfig = self::getOrderAccountType($order_id, 2);
                    $wxConfig = self::getPayConfig('wx', $accountConfig['account']);

                    if (empty($wxConfig))
                    {
                        return $arr;
                    }
                    $data = $WxPay->pay($pay_des, $order_id, $total_amount, 'H5', $wxConfig, $openid, $goods_type);
                    // 下单成功
                    if ($data['return_code'] === 'SUCCESS' && $data['result_code'] === 'SUCCESS')
                    {
                        $orderInfo['wx_url'] = $data['mweb_url'];
                        $orderInfo['wx_type'] = 'MWEB';
                    } else
                    {
                        $ret = [
                            'return_code' => $data['return_code'],
                            'return_msg' => $data['return_msg'],
                        ];

                        return ['status' => false, 'data' => $ret,];
                    }

                }
                ksort($orderInfo);
                $sign = MD5(self::formatBizQueryParaMap($orderInfo, false) . $config_tt['merchant']['app_secret']);
                $orderInfo['sign'] = $sign;
                $orderInfo['risk_info'] = json_encode(['order_id' => $order_id]);

                $arr = [
                    'status' => true,
                    'data' => [
                        'order_info' => $orderInfo,
                        'order_id' => $order_id,
                    ],
                ];

                break;
            default :
                return false;
        }

        return $arr;
    }

    /**
     * 将数组转成uri字符串
     * @param $paraMap
     * @param $urlencode
     * @return false|string
     */
    protected static function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        $reqPar = '';
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if ($urlencode)
            {
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, -1);
        }
        return $reqPar;
    }

    /**
     * 更新准币订单数量和状态(支付成功后)
     * @param string $order_id
     * @param int $order_type 流水类型1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8会员购买 9准币充值 10许愿充值 11赠送
     * @param string $total_amount
     * @param int $uid
     * @param int $consumer_type // 1=>充值 2=>消费,3=>赠送,4=>准币购买申请退款,5=>打赏,6=>苹果充值申请退款
     * @param int $trade_type 支付方式   1=>支付宝,2=>微信支付，3=>余额，4=>准币，5=>京东，6=>微信JSAPI，7=>支付宝手机网页，8=>微信H5，9=>苹果支付，13=>微信小程序支付,14=>广告解锁，15=>微信浏览器打开H5页面使用JSAPI支付
     * @param string $reason 备注
     * @return bool
     */
    public static function UpdateCoin($order_id, $order_type, $total_amount, $uid = 0, $consumer_type = 1, $trade_type = 0, $transactionId = '', $gid = 0, $reason = '')
    {
        //流水类型1=>校准生辰 2=>日运 3=>月运 4=>年运 5=>幸运之城 6=>教学视频 7=>本命预测 8会员购买 9准币充值 10许愿充值
        $ZhunleUserAccountRecordModel = new ZyhsUserAccountRecord();
        $ZhunleUserAccountModel = new ZyhsUserAccount();
        Db::startTrans();
        try
        {
            $coin_num = self::getOrderCoinNum($total_amount);
            //获取准币订单信息
            $records = $ZhunleUserAccountRecordModel->where(['order_id' => $order_id])->column('id,rid,gid,total_amount,order_type,consumer_type,coin_num,uid,order_id');
            if ($consumer_type === 4 || !$records)//新增准币明细
            {
                $user_coin = self::getCoinNum($uid);
                switch ($consumer_type)
                {
                    case 1:
                        $status = 0;
                        $balance = $coin_num + $user_coin;
                        break;
                    case 2:
                    case 5:
                    case 6:
                        $status = 1;
                        $balance = $user_coin - $coin_num;
                        break;
                    case 3:
                    case 4:
                        $status = 1;
                        $balance = $coin_num + $user_coin;
                        break;
                    default:
                        $status = 0;
                        $balance = $user_coin - $coin_num;
                        break;
                }
                $records = [
                    'status' => $status,
                    'account' => 0,
                    'uid' => $uid,
                    'consumer_type' => $consumer_type,
                    'order_type' => $order_type,
                    'gid' => $gid,
                    'pray_date' => 0,
                    'total_amount' => $total_amount,//实际收入
                    'pay_price' => $total_amount,//用户支付
                    'coin_num' => $coin_num,
                    'balance' => $balance,
                    'order_id' => $order_id,
                    'trade_type' => $trade_type,
                    'create_time' => time(),
                    'transaction_id' => $transactionId,
                    'remark' => $reason
                ];
                //增加准币明细记录
                (new ZyhsUserAccountRecord())->SaveData($uid, $records);
            } else //充值时先生成准币明细,回调后更新明细状态
            {
                $records = current($records);
                //更新用户准币明细状态
                $recordData = ['status' => 1, 'update_time' => time(), 'remark' => $reason];
                if ($trade_type)
                {
                    $recordData['trade_type'] = $trade_type;
                }
                if ($transactionId)
                {
                    $recordData['transaction_id'] = $transactionId;
                }
                if ($gid)
                {
                    $recordData['gid'] = $gid;
                }

                (new ZyhsUserAccountRecord())->UpdateData($uid, $records['id'], $recordData, ['order_id' => $records['order_id']]);
            }

            if ($consumer_type === 1)
            {
                $coin_num = $records['coin_num'];
            }
            $ZhunleUserAccountModel->updateCoinInfo($records['uid'], $consumer_type, $coin_num);
            $startTransStatus = true;
            Db::commit();
        }
        catch (Exception $e)
        {
            $startTransStatus = false;
            Log::write($order_id . '更新准币状态和数量失败：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】');
            Db::rollback();
        }
        return $startTransStatus;
    }

    /**
     * 准币支付，检测准币是否充足，并生成明细
     * @param array $orders 一维数组
     * @param string $coinPassword 支付密码
     * @param int $consumer_type 2=>消费 5=>打赏
     * @param string $extend
     * @return bool|int|string
     * @throws ApiException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function checkCoinPayInfo($orders = [], $coinPassword = '', $consumer_type = 2, $extend = '', $verifyPassword = true)
    {
        //检测准币是否充足
        $userCoinInfo = (new ZyhsUserAccount())->getUserCoinFromCache($orders['uid']);
        $param['coin_num'] = self::getOrderCoinNum($orders['total_amount']);
        (new ZyhsUserAccount())->verifyCoinPay($userCoinInfo, $coinPassword, $param['coin_num'], false, $verifyPassword);
        $user_coin = (int)($userCoinInfo['balance'] ?? 0);// 准币余额

        //获取准币订单信息
        $records = (new ZyhsUserAccountRecord())->where(['order_id' => $orders['order_id']])->field('id')->find();
        if (!$records)
        {
            //生成明细
            $insertData = [
                'status' => 0,
                'account' => 0,
                'uid' => $orders['uid'],
                'consumer_type' => $consumer_type,
                'order_type' => $orders['order_type'],
                'gid' => $orders['gid'],
                'pray_date' => 0,
                'total_amount' => $orders['total_amount'],
                'coin_num' => $param['coin_num'],
                'balance' => $user_coin - $param['coin_num'],
                'order_id' => $orders['order_id'],
                'trade_type' => 4,
                'create_time' => time(),
                'extend' => $extend,
            ];
            //增加准币明细记录
            return (new ZyhsUserAccountRecord())->SaveData($orders['uid'], $insertData);
        }
        return $records['id'];
    }

    /**
     * 获取下单描述
     * @param int $orderType
     * @return array
     * @throws ApiException
     * @throws Exception
     */
    public static function createOrderId($orderType = 9)
    {
        switch ($orderType)
        {
            case  9:
                $pay_des = '准有好事 - 准币充值';
                $type = 7;
                break;
            case 100:
                $pay_des = '准有好事 - 连麦咨询';
                $type = 24;
                break;
            case 101:
                $pay_des = '准有好事 - 问答';
                $type = 25;
                break;
            default:
                throw new ApiException('未知的商品');
        }
        $date = (string)(date('YmdHis') - 20000000000000);
        // 生成唯一订单号  011806192149570386 01=>生时校正 02=>运势 03=>占星地图，04=>占星视频，05=>本命预测，06=>vip，07=>准币,08=>商场，09=>定制报告
        $out_trade_no = str_pad($type, 2, '0', STR_PAD_LEFT) . $date . str_pad(random_int(1, 9999), 4, '0', STR_PAD_LEFT);

        return [
            'num' => $type,
            'pay_des' => $pay_des,
            'order_id' => $out_trade_no,
        ];
    }

    /**
     * 获取订单类型和账号类型
     * @param string $order_id
     * @param int $trade_type 1=>支付宝 2=>微信
     * @return array
     * @throws Exception
     */
    public static function getOrderAccountType($order_id = '', $trade_type = 0)
    {
        $num = (int)substr($order_id, 0, 2);
        $trade_type = (int)$trade_type;
        switch (true)
        {
            case in_array($trade_type, [1, 7], true)://支付宝
                $trade_type = 1;
                break;
            case in_array($trade_type, [2, 6, 8, 15], true)://微信
                $trade_type = 2;
                break;
        }
        //支付宝
        if ($trade_type === 1)
        {
            $account = 'alipay_coin';
        } //微信
        elseif ($trade_type === 2)
        {
            $account = 'wx_coin';
        }
        return [
            'num' => $num,
            'account' => $account ?? '',
        ];
    }

    /**
     * 获取支付参数
     * @param $type
     * @param $account
     * @return array|mixed
     * @throws Exception
     */
    public static function getPayConfig($type = 'ali', $account = '')
    {
        $config = (new ZyhsConfig())->configInfo('pay_config');

        if (!empty($config[$account]))
        {
            return json_decode(htmlspecialchars_decode($config[$account]), true);
        }
        throw new ApiException('支付配置缺失' . $account);
    }

    /**
     * 获取用户准币余额
     * @param int $uid
     * @return int
     * @throws Exception
     */
    public static function getCoinNum($uid)
    {
        return (new ZyhsUserAccount())->getCoinNum($uid);
    }

    /**
     * 获取订单准币汇率
     * @param $price
     * @return false|float|int
     */
    public static function getOrderCoinNum($price)
    {
        return $price ? floor($price * 10) : 0;
    }

    /**
     * 获取订单准币金额
     * @param $coin
     * @return false|float|int
     */
    public static function getOrderCoinTotalAmount($coin)
    {
        return $coin ? floor($coin / 10) : 0;
    }
}
